﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace NUnit.Framework
{
    public static class AttributeAssertions
    {
        public static ATTRIBUTE ShouldHaveAttribute<ATTRIBUTE>(this Type type) where ATTRIBUTE : class
        {
            var attributes = Attribute.GetCustomAttributes(type);

            var attr = attributes.FirstOrDefault(a => a is ATTRIBUTE);
            if (attr != null) return attr as ATTRIBUTE;

            Assert.Fail(string.Format("{0} attribute was not found on {1}", typeof(ATTRIBUTE).Name, type.Name));
            return null;
        }

        public static ATTRIBUTE ShouldHaveAttribute<T, ATTRIBUTE>(this Expression<Action<T>> action) where ATTRIBUTE : class
        {
            var info = GetMethodInfo(action);

            var attributes = Attribute.GetCustomAttributes(info);

            var attr = attributes.FirstOrDefault(a => a is ATTRIBUTE);
            if (attr != null) return attr as ATTRIBUTE;

            Assert.Fail(string.Format("{0} attribute was not found on {1}", typeof(ATTRIBUTE).Name, info.Name));
            return null;
        }

        private static MethodInfo GetMethodInfo<T>(Expression<Action<T>> action)
        {
            var body = action.Body as MethodCallExpression;

            if (body == null)
                throw new InvalidOperationException("Expression must be a method call.");

            if (body.Object != action.Parameters[0])
                throw new InvalidOperationException("Method call must target lambda argument.");

            return body.Method;
        }
    }
}